/**
 * @file STL_RtspEnableTest.cpp
 * @author Derek Liu (yjkhtddx@sina.com)
 * @brief 
 * @version 0.1
 * @date 2021-02-08
 * 
 * @copyright Copyright Sci-Tech Laboratories (c) 2021
 * 
 */

#include "STL_RtspEnableTest.h"
#include "STLOG.h"
#include "STL_URL.h"
#include "STL_REGEX.h"
#include "STL_TcpClientMgr.h"
#include "STL_RtspRequest.h"
#include "STL_Timestamp.h"

#define EPOLL_MAX_EVENTS 10 // 单次处理事件个数

namespace STL
{
    STL_RtspEnableTest::STL_RtspEnableTest() : m_usleepTime(1000000) /*1秒*/
    {
        STL_TcpClientMgr::getInstance()->setDataCallback([this](int socket, std::shared_ptr<std::string> sp_data) {
            std::lock_guard<std::mutex> guard(m_testMapMutex);
            std::string rtspUrl = getRtspUrlFromSocket(socket);
            if (rtspUrl.empty())
            {
                stlog_warn("can't find rtspUrl[%d]: %s\n", socket, rtspUrl.c_str());
                return;
            }
            m_testMap.at(rtspUrl)->parser.addData(sp_data); //response处理由此函数调用，所以无需加锁
        });
        STL_TcpClientMgr::getInstance()->setDisconnectCallback([this](int socket) {
            std::lock_guard<std::mutex> guard(m_testMapMutex);
            std::string rtspUrl = getRtspUrlFromSocket(socket);
            if (rtspUrl.empty())
            {
                return;
            }
            else
            {
                reportStatus(rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_ConnectError);
                return;
            }
        });
        m_run = true;
        m_thread = std::thread(std::bind(&STL_RtspEnableTest::checkThread, this));
    }

    void STL_RtspEnableTest::checkThread()
    {
        do
        {
            if (m_usleepTime < 10000)
            {
                m_usleepTime = 10000; //防止脑子抽筋的SB把设置过小的休眠时间
            }
            usleep(m_usleepTime);
            std::lock_guard<std::mutex> guard(m_testMapMutex);
            bool isHaveCheckTarget = false;
            for (auto it = m_testMap.begin(); it != m_testMap.end(); it++)
            {
                auto pInfo = it->second;
                if (pInfo->status == TestStatus_None)
                {
                    if (!isHaveCheckTarget)
                    {
                        isHaveCheckTarget = true;
                        startCheckRtspUrl(it->first);
                    }
                    else
                    {
                        // waiting To Check
                    }
                }
                else
                {
                    if (getTimestamp() - pInfo->timestamp > 20) // 1分钟超时处理
                    {
                        switch (pInfo->status)
                        {
                        case TestStatus_Initing:
                            reportStatus(pInfo->rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_InitError);
                            break;
                        case TestStatus_Connecting:
                            reportStatus(pInfo->rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_ConnectError);
                            break;
                        case TestStatus_Testing:
                            reportStatus(pInfo->rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_TestError);
                            break;
                        default:
                            stlog_error("error status\n");
                            reportStatus(pInfo->rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_TestError);
                            break;
                        }
                    }
                }
            }

            // if(!isHaveCheckTarget){
            //     usleep(15000000);//如果没有检测目标,休眠15秒
            // }

        } while (m_run);
    }

    void STL_RtspEnableTest::startCheckRtspUrl(std::string rtspUrl)
    {
        // 注意 调用函数已加锁 预防死锁
        setStatus(rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_Initing);
        STL_URL::UrlInfo rtspUrlInfo = STL_URL::parseUrl(rtspUrl);
        if (rtspUrlInfo.protocol != "rtsp")
        {
            reportStatus(rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_InitError);
            return;
        }
        m_testMap.at(rtspUrl)->ip = rtspUrlInfo.host;
        if (!STL::STL_REGEX::isIPv4(m_testMap.at(rtspUrl)->ip))
        {
            //TODO 处理域名解析
            reportStatus(rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_InitError);
            return;
        }
        m_testMap.at(rtspUrl)->port = rtspUrlInfo.port;
        if (m_testMap.at(rtspUrl)->port <= 0)
            m_testMap.at(rtspUrl)->port = 554;

        m_testMap.at(rtspUrl)->username = rtspUrlInfo.username;
        m_testMap.at(rtspUrl)->password = rtspUrlInfo.password;

        rtspUrlInfo.username = "";
        rtspUrlInfo.password = "";

        m_testMap.at(rtspUrl)->url = STL::STL_URL::genUrl(rtspUrlInfo);

        m_testMap.at(rtspUrl)->parser.setResponseCallback([this, rtspUrl](std::shared_ptr<STL_RtspResponse> res) {
            // 注意 调用函数已加锁 预防死锁
            if (res->m_code == 200)
            {
                std::string body = res->m_body;
                reportStatus(rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_Successed, body);
                return;
            }
            else if (res->m_code == 401)
            {
                if (m_testMap.at(rtspUrl)->isSendAuth)
                {
                    reportStatus(rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_AuthorzationError);
                    return;
                }

                int cseq = (m_testMap.at(rtspUrl)->cseq)++;
                std::string url = m_testMap.at(rtspUrl)->url;
                STL_RtspRequest req;
                req.setMethod("DESCRIBE");
                req.setUri(url);
                req.setCSeq(cseq);
                req.addHeader("Accept", "application/sdp");
                if (res->getWWWAuthorizationType() == "Basic")
                {
                    std::string realm = res->getWWWAuthorizationMapValue("realm");
                    req.addBasicAuthorization(m_testMap.at(rtspUrl)->username, m_testMap.at(rtspUrl)->password, realm);
                    m_testMap.at(rtspUrl)->isSendAuth = true;
                }
                else if (res->getWWWAuthorizationType() == "Digest")
                {
                    std::string realm = res->getWWWAuthorizationMapValue("realm");
                    std::string nonce = res->getWWWAuthorizationMapValue("nonce");
                    std::string qop = res->getWWWAuthorizationMapValue("qop");
                    std::string algorithm = res->getWWWAuthorizationMapValue("algorithm");
                    std::string opaque = res->getWWWAuthorizationMapValue("opaque");
                    req.addDigestAuthorization(m_testMap.at(rtspUrl)->username, m_testMap.at(rtspUrl)->password, realm, nonce, qop, algorithm, opaque);
                    m_testMap.at(rtspUrl)->isSendAuth = true;
                }
                else
                {
                    reportStatus(rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_AuthorzationError);
                    return;
                }
                std::string message = req.genRequstMessage();
                STL_TcpClientMgr::getInstance()->sendData(m_testMap.at(rtspUrl)->socket, message);
            }
            else
            {
                reportStatus(rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_TestError);
            }
        });

        setStatus(rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_Connecting);
        int socket = STL_TcpClientMgr::getInstance()->createTcpConnect(m_testMap.at(rtspUrl)->ip, m_testMap.at(rtspUrl)->port);
        if (socket <= 0)
        {
            reportStatus(rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_InitError);
            return;
        }
        m_testMap.at(rtspUrl)->socket = socket;
        int cseq = (m_testMap.at(rtspUrl)->cseq)++;
        std::string url = m_testMap.at(rtspUrl)->url;

        STL_RtspRequest req;
        req.setMethod("DESCRIBE");
        req.setUri(url);
        req.setCSeq(cseq);
        req.addHeader("Accept", "application/sdp");
        std::string message = req.genRequstMessage();
        STL_TcpClientMgr::getInstance()->sendData(socket, message);
        setStatus(rtspUrl, STL_RtspEnableTest::TestStatus::TestStatus_Testing);
    }

    void STL_RtspEnableTest::reportStatus(std::string rtspUrl, STL_RtspEnableTest::TestStatus status, std::string sdp /* = ""*/)
    {
        // 注意 调用函数已加锁 预防死锁
        if (status < TestStatus_InitError)
            return;
        if (m_ResultCallback)
            m_ResultCallback(m_testMap.at(rtspUrl)->rtspUrl, status, sdp);
        m_testMap.erase(rtspUrl);
    }

    void STL_RtspEnableTest::setStatus(std::string rtspUrl, STL_RtspEnableTest::TestStatus status)
    {
        if (m_testMap.count(rtspUrl) == 1)
        {
            m_testMap[rtspUrl]->timestamp = getTimestamp();
            m_testMap[rtspUrl]->status = status;
        }
        else
        {
            stlog_warn("error rtspUrl");
        }
    }

    std::string STL_RtspEnableTest::getRtspUrlFromSocket(int socket)
    {
        // 注意 调用函数已加锁 预防死锁
        for (auto it = m_testMap.begin(); it != m_testMap.end(); it++)
        {
            if (it->second->socket == socket)
            {
                return it->second->rtspUrl;
            }
        }
        return "";
    }

    STL_RtspEnableTest::~STL_RtspEnableTest()
    {
    }

    int STL_RtspEnableTest::addTestRtspUrl(std::string rtspUrl)
    {
        std::lock_guard<std::mutex> guard(m_testMapMutex);
        m_testMap[rtspUrl] = new STL_RtspEnableTest::TestInfo(rtspUrl);
        return 0;
    }

} // namespace STL